
package com.espirit.eap.util;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import org.springframework.stereotype.Service;

import com.espirit.eap.manager.I18n;
import com.espirit.eap.manager.I18nService;
import com.googlecode.cswish.model.PageElements;
import com.googlecode.cswish.struts.spring.CacheService;
import com.googlecode.cswish.struts.spring.CacheService.Wrapper;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.ClassUtil;
import com.googlecode.cswish.util.FileUtil;
import com.googlecode.cswish.util.FunctionCallback;
import com.googlecode.cswish.util.bean.ModelUtil;

/**
 * support json conversion
 * 
 * @author jerry.feng
 *
 */
@Service
public class Functions extends com.googlecode.cswish.util.Functions {
	
	private static final Logger logger = Logger.getLogger(Functions.class);
	
	@Resource
	private JSONUtil jsonUtil;
	
	private I18nService i18nService;
	
	private FileUtil fileUtil;
	
	private Wrapper<Properties> jsonProperties;
	
	private I18nService getI18nService() {
		if (i18nService == null) {
			i18nService = ObjectFactory.getInstance().getBean(I18nService.class, false);
		}
		return i18nService;
	}
	
	private FileUtil getFileUtil() {
		if (fileUtil == null) {
			fileUtil = ObjectFactory.getInstance().getBean(FileUtil.class, false);
		}
		return fileUtil;
	}
	
	@PostConstruct
	public void init() {
		super.init();
		
		registExFunction("i18n", new FunctionCallback() {
			@Override
			public Object execute(Object value, Object bean, String property,  Object[] args) {
				if (value == null) {
					return null;
				}
				
				String arg0 = null;
				if (args != null && args.length > 0) {
					arg0 = (String)args[0];
				}
				Object ret;
				if (value instanceof Collection) {
					List<String> i18nList = new ArrayList<>();
					for (Object item : (Collection)value) {
						i18nList.add(getI18n((String)item, arg0));
					}
					ret = i18nList;
				} else {
					ret = getI18n((String)value, arg0);
				}
				return ret;
			}
			
			private String getI18n(String i18nKey, String displayProperty) {
				I18n i18n = getI18nService().getI18n(i18nKey);
				if (i18n != null) {
					if (displayProperty != null) {
						String ret = (String)ModelUtil.getProperty(i18n, displayProperty);
						return ret;
					} else {
						return i18n.getName();
					}
				} else {
					return null;
				}
			}
		});
		// Pinyin property
		registExFunction("py", new FunctionCallback() {
			@Override
			public Object execute(Object value, Object bean, String property,  Object[] args) {
				boolean changed = false;
				if (value instanceof String) {
					String str = ChineseSpeller.convert((String)value, false, true);
					changed = !str.equals(value);
					value = str;
				} else if (value instanceof List) {
					List<Object> list = (List<Object>)value;
					if (list.size() > 0 && list.get(0) instanceof String) {
						List<String> convertList = new ArrayList<String>(list.size());
						for (Object item : list) {
							String str = ChineseSpeller.convert((String)item, false, true);
							convertList.add(str);
							if (!changed) {
								changed = !str.equals(item);
							}
						}
						value = convertList;
					}
				}
				
				// only return the different pinyin property
				return changed ? value : null;
			}
		});
	}
	
	//////////////////////////// json /////////////////////////////////////////
	public String json(Object model) {
		String jsonStr = jsonUtil.json(model);
		return jsonStr;
	}
	
	public String json(Object model, String properties, boolean applyToChildModel, String modelName) {
		String jsonStr = jsonUtil.json(model, properties, applyToChildModel, modelName, false);
		return jsonStr;
	}
	
	/**
	 * Convert the json object according to the property setting
	 * 
	 * @param model
	 * @param actionPath
	 * @return
	 */
	public String json(Object model, String actionPath) {
		if (model == null) {
			return "";
		}
		
		if (jsonProperties == null) {
			CacheService cacheService = ObjectFactory.getInstance().getBean(CacheService.class, true);
			jsonProperties = cacheService.newVar(Properties.class);
		}
		
		Properties properties = jsonProperties.get();
		String jsonPropertyFile = '/' + getSiteName() + "-json.properties";
		if (properties == null || getFileUtil().isChanged(jsonPropertyFile, true)) {
			InputStream input = null;
			try {
				input = this.getClass().getResourceAsStream(jsonPropertyFile);
				properties = new Properties();
				if (input != null) {
					properties.load(input);
				}
				jsonProperties.set(properties);
			} catch (Exception ex) {
				logger.error("Failed to load property file: " + jsonPropertyFile, ex);
			} finally {
				IOUtils.closeQuietly(input);
			}
		}
		
		String property = properties == null ? null : properties.getProperty(actionPath);
		String jsonStr = jsonUtil.json(model, property, false, ClassUtil.getClassName(model), false);
		return jsonStr;
	}
	
	public String json(Object model, String properties, boolean applyToChildModel, String modelName, boolean treatExFuncAsWholeProperty) {
		String jsonStr = jsonUtil.json(model, properties, applyToChildModel, modelName, treatExFuncAsWholeProperty);
		return jsonStr;
	}
	
	public String link(String action, String parameters, boolean noWrap) {
		if (noWrap) {
			return super.link(action, parameters);
		} else {
			return super.link(action, parameters);
		}
	}
	
	public String encode(Object o, String property) {
		if (!(o instanceof String)) {
			return ognlExUtil.convertValue(o, String.class, property);
		}
		
		String s = o.toString();
		StringBuilder sb = new StringBuilder(s.length() + 10);
		for (int i = 0, len = s.length(); i < len; i++) {
			char c = s.charAt(i);
			
			if (c == '\'' || c == '"' || c =='\\') {
				sb.append("\\");
			}
			sb.append(c);
		}
		String value = sb.toString();
		value = StringUtils.replace(value, "\r\n", "\\n");
		value = StringUtils.replace(value, "\n", "\\n");
		return value;
	}
	
	public String encodeHTML(String s) {
		if (s == null) {
			return "";
		}
		
		StringBuilder sb = new StringBuilder(s.length() + 10);
		for (char c : s.toCharArray()) {
			if (c == '\n') {
				sb.append("<br>");
			} else if (c == '\'') {
				sb.append("\\'");
			} else if (c == '"') {
				sb.append("\\\"");
			} else {
				sb.append(c);
			}
		}
		
		return sb.toString();
	}
	
	/////////////////////////// link //////////////////////////////////////////
	@Deprecated
	public String jsLink(String action) {
		return jsLink(action, null);
	}
	
	@Deprecated
	public String jsLink(String action, String parameters) {
		return jsLink(action, parameters, null);
	}
	
	@Deprecated
	public String jsLink(String action, String parameters, String target) {
		StringBuilder sb = new StringBuilder();
		sb.append("javascript:openLink('").append(super.link(action, parameters)).append("'");
		if (target != null) {
			sb.append(",'").append(target).append("'");
		}
		sb.append(");void(0);");
		return sb.toString();
	}
	
	@Deprecated
	public String toExtType(String type) {
		String extType;
		if ("org.apache.struts2.components.TextField".equals(type)) {
			extType = "Ext.form.TextField";
		} else if ("org.apache.struts2.components.Radio".equals(type)) {
			extType = "Ext.form.Radio";
		} else if ("date.ftl".equals(type)) {
			extType = "Ext.form.DateField";
		} else if ("org.apache.struts2.components.TextArea".equals(type)) {
			extType = "Ext.form.TextArea";
		} else if ("org.apache.struts2.components.Select".equals(type)) {
			extType = "Ext.form.ComboBox";
		} else if ("org.apache.struts2.components.Checkbox".equals(type)) {
			extType = "Ext.form.Checkbox";
		} else {
			extType = "Ext.form.TextField";
		}
		return extType;
	}
	
	/**
	 * Convert java data format to Ext format
	 * 
	 * @return
	 */
	public String getExtDataFormat() {
		if ("yyyy-MM-dd".equals(config.getDataFormat())) {
			return "Y-m-d";
		} else {
			return config.getDataFormat();
		}
	}
	
	public Map<String, String> getModelFields(PageElements ps) {
		Map<String, String> modelFields = new HashMap<String, String>();
		String loginId = safeManager.getLoginUser(ServletActionContext.getRequest()).getLoginId();
		for (String name : ps.getAllElements().keySet()) {
			// skip advanceModel
			if (name.startsWith("advanceModel.")) {
				continue;
			}
			
			Boolean hidden = ps.getElement(name).getHidden();
			boolean show = hidden == null || !hidden;
			if (show) {
				int beginIndex = name.indexOf('.');
				if (beginIndex > 0) {
					int endIndex = name.indexOf('.', beginIndex + 1);
					String shortName = null;
					if (endIndex > 0) {					
						shortName = name.substring(beginIndex + 1, endIndex);
						String modelName = beanIntrospector.guessFullClassName(shortName.toLowerCase());
						if (modelName != null) {
							String path = linkModelNoBase(modelName, "_update", true);
							boolean pass = safeManager.checkPage(loginId, path);
							if (pass) {
								String simpleName = getSimpleName(modelName);
								modelFields.put(shortName, simpleName.replace('.', '/'));
							}
						}
					}
				}
			}
		}
		return modelFields;
	}
}